package com.tcl.aota.manage.imp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.perf4j.aop.Profiled;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.tcl.aota.cache.AppCacheManage;
import com.tcl.aota.common.constants.Constants;
import com.tcl.aota.common.utils.StringUtil;
import com.tcl.aota.manage.AppManage;
import com.tcl.aota.manage.OperLogManager;
import com.tcl.aota.persistent.dao.db.AppCurefDao;
import com.tcl.aota.persistent.dao.db.AppDAO;
import com.tcl.aota.persistent.dao.db.AppOsDao;
import com.tcl.aota.persistent.dao.db.PackageDAO;
import com.tcl.aota.persistent.model.App;
import com.tcl.aota.persistent.model.AppCuref;
import com.tcl.aota.persistent.model.AppOs;
import com.tcl.aota.persistent.model.Package;
import com.tcl.aota.persistent.vo.CurefAppInfo;

@Service("appManage")
public class AppManageImp implements AppManage {

    private final static Logger LOG = Logger.getLogger(AppManageImp.class);
    @Resource
    private AppDAO appDAO;

    @Resource
    private PackageDAO packageDAO;

    @Resource
    private AppCacheManage appCacheManage;

    @Resource
    private AppOsDao appOsDao;

    @Resource
    private AppCurefDao appCurefDao;

    @Autowired
    private OperLogManager operLogManager;

    @Override
    public App selectByPrimaryKey(Long id) {
        return appDAO.selectByPrimaryKey(id);
    }

    @Override
    @Profiled
    public List<App> selectAppListByLatestPkg() {
        List<App> appList = null;
        Package latestPackage = null;
        try {
            latestPackage = packageDAO.selectLatestPackage();
            if (latestPackage != null) {
                appList = appDAO.selectAppListByPackageId(latestPackage.getId());
            }
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        return appList;
    }


    @Override
    @Profiled
    public App selectById(Long appId) {
        App app = appCacheManage.getCacheAppDetail(appId);
        if (app == null) {
            synchronized (this) {
                app = appCacheManage.getCacheAppDetail(appId);
                if (app == null) {
                    app = appDAO.selectByPrimaryKey(appId);
                    if (app != null) {
                        LOG.info("GetAppDetail DB," + appId);
                        appCacheManage.hSet(Constants.REDIS.APP, String.valueOf(appId), JSON.toJSONString(app));
                    }
                }
            }
        }
        return app;
    }

    @Override
    public App selectByIdFromDb(Long appId) {
        App app = appDAO.selectByPrimaryKey(appId);
        if (app == null) {
            return null;
        }
        List<AppOs> appOses = appOsDao.selectOsByAppId(appId);
        StringBuilder os = new StringBuilder();
        if (appOses != null) {
            for (AppOs appOs : appOses) {
                os.append(appOs.getOs() + ",");
            }
            if (os.length() > 0) {
                os = os.delete(os.length() - 1, os.length());
            }
            app.setAppOsVersion(os.toString());
        }
        return app;
    }

    @Override
    @Profiled
    @Transactional(propagation = Propagation.REQUIRED)
    public int putOutRecycle(List<Long> ids) {
        int result = Constants.Common.FAIL;
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("ids", ids);
            params.put("trash", Constants.APPStatus.UNTRASH);
            appDAO.trashOpt(params);
            result = Constants.Common.SUCCESS;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        return result;
    }


    @Override
    @Profiled
    @Transactional(propagation = Propagation.REQUIRED)
    public int deleteApps(List<Long> ids) {
        int result = Constants.Common.SUCCESS;
        try {
            //彻底删除aota_app
            appDAO.deleteByIds(ids);
            //删除对应os记录
            appOsDao.deleteAppOsByAppIds(ids);
            //删除对应的发布记录
            appCurefDao.deleteAppCurefByAppIds(ids);
        } catch (Exception e) {
            result = Constants.Common.FAIL;
            LOG.error(e.getMessage(), e);
        }
        return result;
    }


    @Override
    @Profiled
    @Transactional(propagation = Propagation.REQUIRED)
    public int putInRecycle(List<Long> ids, String ip, String account) {
        int result = Constants.Common.FAIL;
        try {
            //不允许删除已发布APP
            List<App> appList = appDAO.selectAppListByIds(ids);
            boolean flag = false;
            for (App app : appList) {
                int status = app.getReleaseStatus();
                if (status == Constants.APPStatus.PUBLISH) {
                    flag = true;
                    break;
                }
            }
            if (flag) {
                result = Constants.Common.DELETE_NOT_ALLOW;
                return result;
            }

            List<Long> subIds = null;
            if (!CollectionUtils.isEmpty(ids)) {
                for (Long id : ids) {
                    List<AppCuref> curefList = appCurefDao.selectCurefByAppId(id);
                    if (CollectionUtils.isEmpty(curefList)) {
                        LOG.warn("App对应的CU信息为空");
                        continue;
                    }

                    subIds = new ArrayList<Long>();
                    subIds.add(id);
                }
            }

            //删除
            Map<String, Object> params = new HashMap<>();
            params.put("ids", ids);
            params.put("trash", Constants.APPStatus.TRASH);
            appDAO.trashOpt(params);
            //删除缓存
            for (Long id : ids) {
                //删除APP,清除该app对应的CU缓存key
                delAppCache(id);
            }

            result = Constants.Common.SUCCESS;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        return result;
    }

    @Override
    @Profiled
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(App app) {
        int num = appDAO.insert(app);
        //插入app和os对应关系
        insertAppOS(app);
        return num;
    }

    @Override
    @Profiled
    @Transactional(propagation = Propagation.REQUIRED)
    public int update(App app) {
        Long id = app.getId();
        int result = appDAO.updateByPrimaryKeySelective(app);
        //app对应的osVersion更改
        appOsDao.deleteAppOsByAppId(app.getId());
        insertAppOS(app);
        //更新APP,删除该app对应的所有缓存key
        //删除APP,清除该app对应的CU缓存key
        delAppCache(id);
        return result;
    }

    /**
     * @param app
     * @throws Exception
     */
    private void insertAppOS(App app) {
        String osVersion = app.getAppOsVersion();
        if (!StringUtils.isEmpty(osVersion)) {
            String[] osArr = osVersion.split(",");
            if (osArr != null && osArr.length > 0) {
                List<AppOs> appOses = new ArrayList<>();
                for (String os : osArr) {
                    AppOs appOs = new AppOs();
                    appOs.setAppId(app.getId());
                    appOs.setOs(os);
                    appOses.add(appOs);
                }
                appOsDao.batchInsert(appOses);
            }
        }
    }

    @Override
    @Profiled
    @Transactional(propagation = Propagation.REQUIRED)
    public int updateAppBySeq(Long appId, int sequence) {
        int result = Constants.Common.FAIL;
        try {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("appId", appId);
            params.put("sequence", sequence);
            appDAO.updateAppBySeq(params);
            //更改排序，清除該App对应的缓存
            delAppCache(appId);
            //删除APP的详情缓存
            result = Constants.Common.SUCCESS;
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
        return result;
    }

    @Override
    public int selectAppCountByConds(Map<String, Object> param) {
        return appDAO.selectAppCountByConds(param);
    }

    @Override
    public List<App> selectAppListByConds(Map<String, Object> param) {
        return appDAO.selectAppListByConds(param);
    }

    @Override
    public App selectAppsByVersion(Map<String, Object> param) {
        App app = null;
        List<App> appList = appDAO.selectAppsByVersion(param);
        if (appList != null && appList.size() > 0) {
            app = appList.get(0);
        }
        return app;
    }

    @Override
    public List<App> selectAppByCurefandOs(String curef, String os) {
        //根据curef和os查询到数据是0条，则缓存10s钟返回0条，缓存消失再查询数据
        String checkKey = StringUtil.append(curef, "_", os);
        String value = appCacheManage.get(checkKey);
        if (!StringUtils.isEmpty(value)) {
            LOG.info("数据为空缓存30s，" + checkKey);
            return null;
        }
        List<App> appList = appCacheManage.hGetAppCache(curef, os);
        //获取缓存,缓存获取不到，读取数据库
        if (StringUtil.isNullList(appList)) {
            synchronized (this) {
                appList = appCacheManage.hGetAppCache(curef, os);
                if (StringUtil.isNullList(appList)) {
                    appList = appDAO.selectAppByCurefandOs(curef, os);
                }
                if (!StringUtil.isNullList(appList)) {
                    appCacheManage.hSetAppCache(curef, os, appList);
                    LOG.info("getAppsDB,set redis cache,num=" + appList.size());
                } else {
                    //查询到结果为空，避免重复sql查询，设置10s缓存
                    LOG.info("getAppsDb为0，设置数据为空的缓存," + checkKey);
                    appCacheManage.set(checkKey, checkKey);
                    appCacheManage.expire(checkKey, 30);
                }
            }
        }
        return appList;
    }



    /**
     * del relevant cache when del or edit app
     *
     * @param appId
     */
    private void delAppCache(Long appId) {
        try {
            appCacheManage.hDel(Constants.REDIS.APP, String.valueOf(appId));
            List<AppCuref> curefs = appCurefDao.selectCurefByAppId(appId);
            if (StringUtil.isNullList(curefs)) {
                return;
            }
            for (AppCuref appCuref : curefs) {
                String curef = appCuref.getCuref();
                appCacheManage.delCache(curef);
            }
            LOG.info("Del App Cu Cache,appId:" + appId + ",curef size:" + curefs.size());
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /**
     * 删除Cu集合缓存
     *
     * @param curefs
     */
    private void delCuCache(List<String> curefs) {
        if (StringUtil.isNullList(curefs)) {
            return;
        }
        for (String cu : curefs) {
            appCacheManage.delCache(cu);
        }
        LOG.info("Del Cu Cache:" + curefs.size());
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void publishApp(List<Long> appIds, List<String> curefs, String ip, String account) {
        //清除发布cu的全部缓存
        delCuCache(curefs);
        for (Long appId : appIds) {
            //该App已发布的CU
            App app = appDAO.selectByPrimaryKey(appId);
            if (app == null) {
                continue;
            }
            //判断新增发布的CU
            List<AppCuref> appCurefs = new ArrayList<>();
            List<AppCuref> appCurefsDb = appCurefDao.selectCurefByAppId(appId);
            for (String curef : curefs) {
                AppCuref appCuref = new AppCuref();
                appCuref.setAppId(appId);
                appCuref.setCuref(curef);
                if (CollectionUtils.isEmpty(appCurefsDb) || !appCurefsDb.contains(appCuref)) {
                    appCurefs.add(appCuref);
                }
            }
            if (!CollectionUtils.isEmpty(appCurefs)) {
                LOG.info("需发布CU:" + appCurefs.size() + ",appId:" + appId);
                unPublishLowApp(appCurefs, app, ip, account);
            }
        }
    }

    /**
     * 取消该cu已发布的低版本app
     *
     * @param appCurefs
     * @param app
     */
    private void unPublishLowApp(List<AppCuref> appCurefs, App app, String ip, String account) {
        String apkPkgName = app.getApkPackName();
        int versionCode = StringUtil.obj2int(app.getVersionCode());
        //删除CU发布的低版本apk
        List<AppCuref> delCus = new ArrayList<>();
        //新增CU发布的高版本apk
        List<AppCuref> addCus = new ArrayList<>();
        for (AppCuref appCuref : appCurefs) {
            String cu = appCuref.getCuref();
            //通过cu查找已发布的同包的apk
            List<CurefAppInfo> appInfos = appCurefDao.selectAppByCuapkPkg(cu, apkPkgName);
            //同包没有已发布的apk
            if (CollectionUtils.isEmpty(appInfos)) {
                addCus.add(appCuref);
                continue;
            }
            //同包有发布的apk,按照versionCode高到低排序
            appInfos = sort(appInfos);
            for (int i = 0; i < appInfos.size(); i++) {
                CurefAppInfo appInfo = appInfos.get(i);
                if (i == 0) {
                    int version = StringUtil.obj2int(appInfo.getVersionCode());
                    //现发布的高版本，删除低版本，发布高版本
                    if (version < versionCode) {
                        AppCuref delCuref = new AppCuref();
                        delCuref.setAppId(appInfo.getAppId());
                        delCuref.setCuref(cu);
                        delCus.add(delCuref);
                        addCus.add(appCuref);
                    }
                    //现发布低版本，则忽略
                    continue;
                }
                //删除cu发布的多个低版本
                AppCuref delCuref = new AppCuref();
                delCuref.setAppId(appInfo.getAppId());
                delCuref.setCuref(cu);
                delCus.add(delCuref);
            }
        }
        //新增cu
        if (addCus.size() > 0) {
            int count = appCurefDao.batchInsert(addCus);
            //更新App状态为pulished
            if (count > 0) {
                appDAO.publishApp(app.getId(), Constants.APPStatus.PUBLISH);

                //新发布的应用记录日志操作记录操作日志
                if (!CollectionUtils.isEmpty(addCus)) {
                    try {
                        operLogManager.recordOperLog(ip, account, Constants.APPStatus.PUBLISH,
                            "", addCus, app.getId());
                    } catch (Exception e) {
                        LOG.error(e.getMessage(), e);
                    }
                }
            }
            LOG.info("publish count:" + count);
        }
        //删除cu
        for (AppCuref curef : delCus) {
            int num = appCurefDao.deleteByPrimaryKey(curef);
            if (num > 0) {
                Long unAppId = curef.getAppId();
                List<AppCuref> curefList = appCurefDao.selectCurefByAppId(unAppId);
                if (CollectionUtils.isEmpty(curefList)) {
                    appDAO.publishApp(unAppId, Constants.APPStatus.UN_PUBLISH);
                    LOG.info("unPublish app:" + unAppId);

                    //记录升级时需要移除的CU日志
                    try {
                        operLogManager.recordOperLog(ip, account, Constants.APPStatus.UN_PUBLISH,
                            Constants.Reason.CU_UPGRADE, delCus, unAppId);
                    } catch (Exception e) {
                        LOG.error(e.getMessage(), e);
                    }
                }
            }
        }
    }

    /**
     * 按versionCode按低到高排序
     *
     * @param list
     * @return
     */
    public List<CurefAppInfo> sort(List<CurefAppInfo> list) {
        Collections.sort(list, new Comparator<CurefAppInfo>() {
            public int compare(CurefAppInfo arg0, CurefAppInfo arg1) {
                int versionCode0 = StringUtil.obj2int(arg0.getVersionCode());
                int versionCode1 = StringUtil.obj2int(arg1.getVersionCode());
                if (versionCode0 > versionCode1) {
                    return 1;
                } else if (versionCode0 < versionCode1) {
                    return -1;
                }
                return 0;
            }
        });
        return list;
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void unPublishApp(List<Long> appIds, String ip, String account) {
        for (Long appId : appIds) {
            appDAO.publishApp(appId, Constants.APPStatus.UN_PUBLISH);

            //记录日志
            try {
                List<AppCuref> curefList = appCurefDao.selectCurefByAppId(appId);
                operLogManager.recordOperLog(ip, account, Constants.APPStatus.UN_PUBLISH,
                    Constants.Reason.UNPUBLISH, curefList, appId);
            } catch (Exception e) {
                LOG.error(e.getCause(), e);
            }

            delAppCache(appId);
            LOG.info("Operate Unpublish Success!appID=" + appId);
        }
        appCurefDao.deleteAppCurefByAppIds(appIds);
    }


    @Override
    public int deleteAppCurefByAppIdAndCus(Long appId, List<String> curefs, String ip, String account) {
        int result = Constants.Common.FAIL;
        try {
            LOG.info("删除appId的CU,appID:" + appId + ",Cu列表:" + curefs);
            //清除缓存
            delAppCache(appId);
            //清除数据库
            appCurefDao.deleteAppCurefByAppIdAndCus(appId, curefs);
            List<AppCuref> curefList = appCurefDao.selectCurefByAppId(appId);
            if (curefList == null || curefList.size() == 0) {
                appDAO.publishApp(appId, Constants.APPStatus.UN_PUBLISH);
            }

            try {
                operLogManager.recordOperLogByString(ip, account,
                    Constants.APPStatus.UN_PUBLISH, Constants.Reason.DELETE, curefs, appId);
            } catch (Exception e) {
                LOG.error("记录操作日志失败:" + e.getMessage(), e);
            }


            result = Constants.Common.SUCCESS;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }
        return result;
    }
}
